package com.jprocms.module.cms.dal.dataobject.site;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.text.StrJoiner;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.jpro.framework.mybatis.core.type.JsonLongSetTypeHandler;
import com.jprocms.module.cms.api.dto.SiteConfigDto;
import com.jprocms.module.cms.api.dto.SiteConfigStaticPageDto;
import com.jprocms.module.cms.dal.dataobject.globalconfig.GlobalConfigDO;
import com.jprocms.module.cms.dal.dataobject.sitetpl.SiteTplDO;
import com.jprocms.module.cms.enums.EnumIntCommonStatus;
import com.jprocms.module.cms.enums.EnumPlatform;
import com.jprocms.module.cms.framework.mybatisplus.ObjectJacksonTypeHandler;
import com.jprocms.module.cms.framework.tree.TreeDo;
import com.jprocms.module.cms.util.CmsContextUtil;
import com.jprocms.module.cms.web.IUrl;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.Nullable;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import static com.jprocms.module.cms.constant.CmsSysConstants.*;
import static com.jprocms.module.cms.constant.StaticPageConstants.*;

/**
 * 站点 DO
 *
 * @author jprocms
 */
//mybatis-plus如果属性里指定了TypeHandler，需要开启autoResultMap=true，才会正常处理,这里采用mybatis xml方式加载对象  取消 autoResultMap
@TableName(value = "cms_site")
@KeySequence("cms_site_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库，可不写。
@Data
@Builder
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(value = "handler")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class SiteDO extends TreeDo implements IUrl, Serializable {

    private static final long serialVersionUID = 1L;
    public static final String DOUBLE_SLASH = "//";
    /**
     * ID
     */
    @TableId
    private Long id;
    /**
     * 站点名称
     */
    private String name;
    /**
     * 站点协议
     */
    private String protocol;
    /**
     * 站点目录名
     */
    private String sitePath;
    /**
     * 站点访问子目录(内网区分站点访问)
     */
    private String siteVisitPath;
    /**
     * 站点域名
     */
    private String siteDomain;
    /**
     * 域名别名
     */
    private String siteAliasDomain;
    /**
     * 发布平台JSON数组 ID来自数据字典
     */
    @JsonIgnore
    @TableField(typeHandler = JsonLongSetTypeHandler.class)
    private Set<Long> staticpagePlatforms;
    /**
     * 静态页生成挂载FTP数组
     */
    @JsonIgnore
    @TableField(typeHandler = JsonLongSetTypeHandler.class)
    private Set<Long> staticpageFtpIds;
    /**
     * 上传挂载FTP数组
     */
    @JsonIgnore
    @TableField(typeHandler = JsonLongSetTypeHandler.class)
    private Set<Long> resFtpIds;
    /**
     * 站点状态（1开启  0 关闭）
     */
    private Integer status;

    /**
     * 是否允许推送(1允许 0 不允许)
     */
    private Integer allowPush;
    /**
     * SEO标题
     */
    private String seoTitle;
    /**
     * SEO关键字
     */
    private String seoKey;
    /**
     * SEO描述
     */
    private String seoDesc;
    /**
     * 站点配置
     */
    @TableField(typeHandler = ObjectJacksonTypeHandler.class)
    private SiteConfigDto siteConfig;
    /**
     * 全局对象
     */
    @TableField(exist = false)
    private GlobalConfigDO globalConfigDO;

    private Integer globalConfig;
    /**
     * 父站点ID
     */
    private Long parentId;
    /**
     * 排序
     */
    private Integer sort;
    /**
     * pc模板方案
     */
    private String pcSolution;
    /**
     * h5模板方案
     */
    private String h5Solution;

    /**
     * 总访问量
     */
    private Long views;
    /**
     * 今年访问量
     */
    private Long yearViews;
    /**
     * 昨日访问量
     */
    private Integer yesterdayViews;
    /**
     * 今日访问量
     */
    private Integer todayViews;
    /**
     * 在线访客人数
     */
    private Integer uvCount = 0;

    private String staticHtml;

    private String h5StaticHtml;

    @TableField(exist = false)
    private List<SiteTplDO> siteTplDOS;

    public void init() {
        setViews(0L);
        setTodayViews(0);
        setYesterdayViews(0);
        setYearViews(0L);
        setSort(10);
    }

    @JsonIgnore
    public boolean getPushSubmitWorkflow() {
        return getSiteConfig().getBase().getPushNeedCheck();
    }

    @JsonIgnore
    public boolean getOpenSiteStaticPage() {
        return getSiteConfig().getStaticPage().getOpenSiteStaticPage();
    }

    @JsonIgnore
    public boolean getEnabledAndAuto() {
        final SiteConfigStaticPageDto staticPageConfig = getSiteConfig().getStaticPage();
        return staticPageConfig.getOpenSiteStaticPage() && staticPageConfig.getOpenAuto();
    }

    @JsonIgnore
    @JSONField(serialize = false)
    public String getTplPath() {
        return TPL_BASE + SPT + getSitePath();
    }


    @JsonIgnore
    @JSONField(serialize = false)
    public String getPcSolutionPath() {
        return getTplPath() + SPT + getPcSolution();
    }

    @JsonIgnore
    @JSONField(serialize = false)
    public String getH5SolutionPath() {
        return getTplPath() + SPT + getH5Solution();
    }

    /**
     * 获取资源目录到站点目录  /res/www/
     *
     * @return
     */
    @JsonIgnore
    @JSONField(serialize = false)
    public String getResourcePath() {
        return RES_PATH + SPT + getSitePath();
    }

    /**
     * URL地址
     */
    @JsonIgnore
    @Schema(description = "URL地址")
    public String getUrl() {
        return getOpenSiteStaticPage() ? getStaticUrl() : getDynamicUrl();
    }

    /**
     * 动态URL地址
     */
    @Schema(description = "动态URL地址")
    @JsonIgnore
    public String getDynamicUrl() {
        StrJoiner append = StrJoiner.of(CharSequenceUtil.EMPTY).append(getProtocol()).append(getSiteDomain());
        Integer port = getGlobalConfigDO().getPort();
        if (port != null && !Arrays.asList(443, 80).contains(port)) {
            append.append(":").append(port.toString());
        }
        return append.toString();
    }

    /**
     * 静态URL地址
     */
    @JsonIgnore
    @Schema(description = "静态URL地址")
    public String getStaticUrl() {
        return CmsContextUtil.isPc() ? getPcStaticUrl() : getMobileStaticUrl();
    }

    /**
     * PC端静态URL地址
     */
    @JsonIgnore
    @Schema(description = "PC端静态URL地址")
    public String getPcStaticUrl() {
        return getPcStaticUrl(CharSequenceUtil.EMPTY);
    }

    /**
     * 手机端静态URL地址
     */
    @JsonIgnore
    @Schema(description = "手机端静态URL地址")
    public String getMobileStaticUrl() {
        return getMobileStaticUrl(CharSequenceUtil.EMPTY);
    }

    public String getPcStaticUrl(String url) {
        return getStaticUrl(getGlobalConfigDO().getHtmlPrefix(), false, url);
    }

    public String getMobileStaticUrl(String url) {
        if (!hasH5DifferentSolution()) {
            return getPcStaticUrl(url);
        }
        return getStaticUrl(getGlobalConfigDO().getHtmlPrefix(), true, url);
    }

    private String getStaticUrl(String storageUrl, boolean isMobile, String url) {
        StrJoiner builder = StrJoiner.of(CharSequenceUtil.EMPTY);
        if (!StringUtils.startsWithAny(storageUrl, HTTP_PROTOCOL, HTTPS_PROTOCOL)) {
            builder.append(getProtocol()).append(getSiteDomain()).append(":").append(getGlobalConfigDO().getPort().toString());
        }
        builder.append(storageUrl);
        builder.append(SPT).append(getSitePath());
        builder.append(isMobile ? MOBILE_PATH : CharSequenceUtil.EMPTY);
        builder.append(url);
        return builder.toString();
    }

    @JsonIgnore
    public String getPcStaticIndexFilePath() {
        return getPcStaticPath(DEFAULT_INDEX_PAGE);
    }

    @JsonIgnore
    public String getH5StaticIndexFilePath() {
        return getH5StaticPath(DEFAULT_INDEX_PAGE);
    }

    public String getPcStaticPath(String path) {
        return getStaticPath(false, path);
    }

    public String getH5StaticPath(String path) {
        return getStaticPath(true, path);
    }

    @JsonIgnore
    public String getStaticBase() {
        return getStaticPath(false, CharSequenceUtil.EMPTY);
    }

    public String getStaticPath(boolean isH5, String path) {
        StrJoiner builder = StrJoiner.of(CharSequenceUtil.EMPTY);
        builder.append(getSitePath());
        builder.append(isH5 ? MOBILE_PATH : CharSequenceUtil.EMPTY);
        builder.append(path);
        return builder.toString();
    }

    public static String getChannelFilePath(String channelPath, long page) {
        String path = StringUtils.replaceEach(STATIC_FILE_RULE_CHANNEL,
                new String[]{PATH_CHANNEL_PATH}, new String[]{channelPath});
        if (page > 1) {
            return path + "_" + page + SUFFIX;
        }
        return path + SUFFIX;
    }

    public static String getContentFilePath(Long channelId, String channelPath, Long contentId, LocalDateTime createdDate, long page) {
        String year = String.valueOf(createdDate.getYear());
        String month = StringUtils.leftPad(String.valueOf(createdDate.getMonthValue()), 2, '0');
        String day = StringUtils.leftPad(String.valueOf(createdDate.getDayOfMonth()), 2, '0');
        String url = StringUtils.replaceEach(STATIC_FILE_RULE_CONTENT,
                new String[]{PATH_CHANNEL_PATH, PATH_CONTENT_ID, PATH_YEAR, PATH_MONTH, PATH_DAY},
                new String[]{channelPath, String.valueOf(contentId), year, month, day});
        if (page > 1) {
            return url + "_" + page + SUFFIX;
        }
        return url + SUFFIX;
    }

    /**
     * 获取动态地址，含子目录，不含域名、端口。如：{@code /siteVisitPath}
     * 动态地址用于拼接动态页面地址，如留言、搜索页面等。PC站和手机站可拥有不同域名，所以不带域名信息。
     */
    @JsonIgnore
    @Schema(description = "动态地址，不含域名、端口。如：{@code /siteVisitPath}")
    public String getBase() {
        StrJoiner buff = StrJoiner.of(CharSequenceUtil.EMPTY);
        if (StringUtils.isNotBlank(getSiteVisitPath())) {
            buff.append(SPT).append(getSiteVisitPath());
        }
        return buff.toString();
    }

    /**
     * 获取 API 接口地址。如：{@code /front}
     */
    @JsonIgnore
    @Schema(description = "API 接口地址。如：{@code /front}")
    public String getFrontApi() {
        StrJoiner buff = StrJoiner.of(CharSequenceUtil.EMPTY);
        buff.append(FRONT_API);
        return buff.toString();
    }

    @JsonIgnore
    public String getTpl() {
        return joinTemplate(TPL_INDEX + SPT + getIndexTplAdapter());
    }

    /**
     * 获取首页模板路径 不带模板方案文件夹（/channel/news.html）
     *
     * @return
     */
    @Schema(description = "首页模板")
    @Nullable
    @JsonIgnore
    public String getIndexTplAdapter() {
        List<SiteTplDO> siteTplDOList = getSiteTplDOS();
        SiteTplDO pcSiteTpl = CollUtil.findOne(siteTplDOList, record -> record.getPlatformId().equals(EnumPlatform.PC.getPlatform()));
        SiteTplDO h5SiteTpl = CollUtil.findOne(siteTplDOList, record -> record.getPlatformId().equals(EnumPlatform.H5.getPlatform()));
        if (pcSiteTpl == null) {
            return TPL_INDEX;
        }
        return CmsContextUtil.isPc() ? pcSiteTpl.getIndexTpl() : h5SiteTpl.getIndexTpl();
    }

    /**
     * 拼接模板相对路径
     * 例如首页 /sitepath/default/index/index
     * 栏目页面 /sitepath/default/channel/index
     *
     * @param template
     * @return
     */
    public String joinTemplate(String template) {
        return SPT + getSitePath() + SPT + (CmsContextUtil.isPc() ? getPcSolution() : getH5Solution()) + SPT + template;
    }

    /**
     * 获取模板引用样式图片路径。如：/res/www/default/
     */
    @JsonIgnore
    @Schema(description = "模板文件路径。如：/res/www/default/")
    public String getResPath() {
        StrJoiner builder = StrJoiner.of(CharSequenceUtil.EMPTY);
        //ftp模板资源的话暂时通过nginx来解决 这里暂时不拼接资源访问域名
        //builder.append(getGlobalConfig().getTplStoreConfigDomain());
        return builder.append(RES_PATH).append(SPT)
                .append(getSitePath()).append(SPT)
                .append(CmsContextUtil.isPc() ? getPcSolution() : getH5Solution())
                .toString();
    }

    /**
     * 获取模板引用样式图片路径。如：/res/www/default/
     */
    @JsonIgnore
    @Schema(description = "模板文件路径。如：/res/www/default/")
    public String getH5ResPath() {
        StrJoiner builder = StrJoiner.of(CharSequenceUtil.EMPTY);
        //builder.append(getGlobalConfig().getTplStoreConfigDomain());
        return builder
                .append(RES_PATH).append(SPT)
                .append(getSitePath()).append(SPT)
                .append(getH5Solution())
                .toString();
    }


    /**
     * 是否单独设置h5模板
     *
     * @return
     */
    @JsonIgnore
    public boolean hasH5DifferentSolution() {
        return !getPcSolution().equals(getH5Solution());
    }

    @JsonIgnore
    public boolean getCommentEnable() {
        return EnumIntCommonStatus.OPEN.getStatus().equals(getSiteConfig().getComment().getCommentStatus());
    }

    /**
     * 是否阻止内容复制
     *
     * @return
     */
    public boolean getPreventCopy() {
        return Optional.ofNullable(getSiteConfig().getBase().getPreventCopy()).orElse(false);
    }

    /**
     * 是否混淆内容
     *
     * @return
     */
    public boolean getConfuseContent() {
        return Optional.ofNullable(getSiteConfig().getBase().getConfuseContent()).orElse(false);
    }

}
